<?php

/**
 * Description of pte2tex
 *
 * @author Ryan
 *
 * This class contains:
 * writeFX()
 * writeBeatFX()
 * GetBeatFX()
 * GetNoteFX()
 * GetBarProperties()
 */
class pte2tex {
	protected $currentTempo = NULL;
	protected $currentTimeSig = NULL;
	protected $currentKeySig = NULL;
	//Stores the name of the key for major keys
	protected $MAJOR = array('C','G','D','A','E','B','F#','C#',
							'F','Bb','Eb','Ab','Db','Gb','Cb');
	//Stores the name of the key for minor keys
	protected $MINOR = array('Am','Em','Bm','F#m','C#m','G#m','D#m','A#m',
							'Dm','Gm','Cm','Fm','Bbm','Ebm','Abm');
	// Stores if a note is bent at any point
	protected $bent = array(0,0,0,0,0,0,0);
	//This is used for acciaccatura's, which need to be put on the next note
	//This is probably kludgy
	protected $nextNoteFX = array(array(),array(),array(),array(),array(),array(),array());
	protected $stringCount = 6;

	/**
	 *
	 * @param Position $position
	 * @param Note $note
	 * @author Ryan
	 * @since 26/06/2011
	 *
	 * Writes the effects for a note or a beat
	*/
    function writeFX(Position $position,Note $note){
		//If the previous note was an acciaccatura, this needs to be added
		$outputEnd = $this->nextNoteFX[$note->GetString()];
		$output = array();
		//And then reset the array
		$this->nextNoteFX[$note->GetString()] = array();
		//Do the beat FX
		$this->GetBeatFX($position,$output);
		/* Do all the note effects
		 * i.e. all the things that apply to an individual string
		 */
		$this->GetNoteFX($note,$output);
		//This is a special case, it must go on the next note's effect
		//Write the output
		if(count($output)>= 1){
			printf("{%s}",implode(" ",array_merge($output,$outputEnd)));
		}
	}
	/**
	 *
	 * @param Position $position
	 * @param Note $note
	 * @since 28/06/2011
	 * @author Ryan
	 *
	 * If the note is an acciaccatura, it should call this function instead of
	 * writeFX, as nothing should be written
	 */
	function IsAcciaccatura(Position $position,Note $note){
		//If the previous note was an acciaccatura, this needs to be added
		$output = $this->nextNoteFX[$note->GetString()];
		//And then reset the array
		$this->nextNoteFX[$note->GetString()] = array();
		//Do the beat FX
		
		$this->GetBeatFX($position,$output);
		/* Do all the note effects
		 * i.e. all the things that apply to an individual string
		 */
		//TEMPORARILY STOPPED until alphaTex is updated to allow bends
		//$this->GetNoteFX($note,$output);
		if($position->IsAcciaccatura()){
			//Get the string it is on, push this onto the right spot in our array
			array_push($output,sprintf("gr %d",$note->GetFretNumber()));
			array_push($this->nextNoteFX[$note->GetString()],implode(" ",$output));
		}
		//Do nothing, you can't output anything for alphaTex if it is an
		//acciaccatura
	}
	/**
	 * @param Position $position
	 * @author Ryan
	 * @since 28/06/2011
	 *
	 * Writes only the beat FX
	 */
	function writeBeatFX(Position $position){
		$output = array();
		$this->GetBeatFX($position,$output);
		if(count($outString)>= 1){
			printf("{%s}",implode(" ",$outString));
		}
	}

	/**
	 *
	 * @param Position $position
	 * @param array &$array
	 * @author Ryan
	 * @since 28/06/2011
	 *
	 * BeatFX apply to the whole note or chord
	*/
	function GetBeatFX(Position $position,array &$array ){
		if($position->HasLetRing()){
			array_push($array,"lr");
		}
		if($position->HasVibrato() || $position->HasWideVibrato()){
			array_push($array,"v");
		}
		if($position->HasArpeggioDown()){
			array_push($array,"sd");
		}
		if($position->HasArpeggioUp()){
			array_push($array,"su");
		}
		if($position->HasTremoloPicking()){
			array_push($array,"tp");
		}
		if($position->IsDotted()){
			array_push($array,"d");
		}
		if($position->IsDoubleDotted()){
			array_push($array,"dd");
		}
		if($position->HasPalmMuting()){
			array_push($array,'pm');
		}
		if($position->IsStaccato()){
			array_push($array,"st");
		}
		// Do tuplets and odd notes, but we will only focus on tuplets
		//This could be HORRIBLY wrong, do not trust this if statement
		if($position->HasIrregularGroupingTiming()){
			$notesPlayed = NULL;
			$notesPlayedOver = NULL;
			$position->GetIrregularGroupingTiming($notesPlayed,$notesPlayedOver);
			if($notesPlayed == 3 && $notesPlayedOver == 3){
				array_push($array,"tu 3");
			}
		}
		if($position->IsIrregularGroupingStart() || $position->IsIrregularGroupingMiddle() || $position->IsIrregularGroupingEnd()){
			array_push($array,"tu 3");
		}
	}

	/**
	 * @param Note $note
	 * @param array &$array
	 * @author Ryan
	 * @since 28/06/2011
	 *
	 * Gets the FX for the given note
	 */
	function GetNoteFX(Note $note,array &$array){
		//Pull off's from nowhere don't apply either so do it for both
		if($note->HasPullOff() || $note->HasPullOffToNowhere()){
			array_push($array,"p");
		}
		if($note->HasHammerOn() || $note->HasHammerOnFromNowhere()){
			array_push($array,"h");
		}
		if($note->IsNaturalHarmonic()){
			array_push($array,'nh');
		}
		//Not sure how to write artificial harmonics in alphaTex
//		if($note->HasArtificialHarmonic()){
//			array_push($array,sprintf("ah %d",$note->HasArtificialHarmonic()));
//		}
		if($note->HasTappedHarmonic()){
			$fretNumber = 0;
			$note->GetTappedHarmonic(&$fretNumber);
			array_push($array,sprintf("ah %d",$fretNumber));
		}
		if($note->HasTrill()){
			$trillFret;
			$note->GetTrill(&$trillFret);
			array_push($array,sprintf("tr %d",$trillFret));
		}
		// USE THIS IF YOU MODIFY ALPHATAB
//		if($note->IsMuted()){
//			array_push($array,"x");
//		}
		//Do some sliding
		//Slides is not 100% correct at the moment
		if($note->HasSlide()){
			//Why doesn't "ss" work?
			array_push($array,"sl");
		}
		if($note->HasSlideInto() || $note->HasSlideOutOf()){
			array_push($array,"sl");
		}
		//Now the "fun" part, bends
		//Check if a bend is still happening
//		if($this->bent[$note->GetString()] !==0){
//			array_push($array,sprintf('b(%1$d %1$d)',$this->bent[$note->GetString()]));
//		}
		if($note->HasBend()){
			//We can only do two things in alphaTab currently as far as I know,
			//start and end bends, plus anything imbetween
			//to hold a bend
			$type = 0;
			$releasePitch = 0;
			$bentPitch = 0;
			$duration = 0;
			$drawStartPoint = 0;
			$drawEndPoint = 0;
			$note->GetBend(&$type,&$bentPitch,&$releasePitch,&$duration,&$drawStartPoint,&$drawEndPoint);
			//Testing dump $note
//			printf("<BR>");
//			var_dump($type,$releasePitch,$bentPitch);
//			printf("<BR>");
			switch($type){
				case 0://Normal Bend and hold for that note
					array_push($array,sprintf("b(0 %d)",$bentPitch));
				break;
				case 1://Bend and release in same note
					array_push($array,sprintf("b(0 %d %d)",$bentPitch,$releasePitch));
				case 2://Bend and hold
					//Find the string and push onto the "bent" array
					array_push($array,sprintf("b(0 %d)",$bentPitch));
					$this->bent[$note->GetString()] = $bentPitch;
				break;
				//Guessing cases' 3,4,5 are pre bends from PTE dialog
				case 3://Pre-bend
					array_push($array,sprintf('b(%1$d %1$d)',$bentPitch));
				break;
				case 4://Pre-bend and release
					array_push($array,sprintf("b(%d %d)",$bentPitch,$releasePitch));
				break;
				case 5://Pre-bend and hold
					array_push($array,sprintf("b(0 %d)",$bentPitch));
				break;
				case 6://Release Gradual
					array_push($array,sprintf("b(%d %d)",$this->bent[$note->GetString()],$releasePitch));
				break;
				case 7://Release Immediate (I'm Guessing not tested)
					array_push($array,sprintf('b(%1$d %1$d)',$this->bent[$note->GetString()]));
					$this->bent[$note->GetString()] = 0;
				break;
			}
		}
		
	}
	/**
	 * @param Barline $barline
	 * @since 28/06/2011
	 * @author Ryan
	 *
	 * Gets the bar properties including Key signature, Time Signature
	 */
	function GetBarProperties(Barline $barline){
		
		$outString = "";
		// There is a bug here where it outputs it randomly, but this doesn't
		// 100% need to be fixed as alphatab ignores redeclarations of the
		// same key signature
		$keySig = $barline->GetKeySignature();
		if($keySig->IsMajorKey()){
			$keyName = $this->MAJOR[$keySig->GetKeyAccidentals()];
		}else{
			$keyName = $this->MINOR[$keySig->GetKeyAccidentals()];
		}
		//Only output if it has changed
		if($keyName !== $this->currentKeySig){
			$outString .= sprintf("\\ks %s ",$keyName);
			$this->currentKeySig = $keyName;
		}
		//Do the time signature
		$timeSig = $barline->GetTimeSignature();
		if($timeSig->IsCommonTime()){
			$timeName = "c";
		}else{
			$timeName = sprintf("%d %d",$timeSig->GetBeatAmount(),$timeSig->GetBeatsPerMeasure());
		}
		//Only do it if it has changed
		if($timeName !== $this->currentTimeSig){
			$outString .= sprintf("\\ts %s",$timeName);
			$this->currentTimeSig = $timeName;
		}
		return $outString;
	}
	/**
	 * @param PowerTabFileHeader &$header
	 * @author Ryan
	 * @since 26/06/2011
	 *
	 * Writes the file information to alphaTex
	 */
	function writeSongInfo(PowerTabFileHeader &$header){
		if($header->IsSong()){
			if($header->GetSongTitle() !== ""){
				printf("\\title \"%s\"<br>\n",$header->GetSongTitle());
			}
			if($header->GetSongArtist() !== ""){
				printf("\\artist \"%s\"<br>\n",$header->GetSongArtist());
			}
			if($header->GetSongAudioReleaseTitle() !== ""){
				printf("\\album \"%s\"<br>\n",$header->GetSongAudioReleaseTitle());
			}
		}else if($header->IsLesson()){
			//Is a lesson
			if($header->GetLessonTitle() !== ""){
				printf("\\title \"%s\"<br>\n",$header->GetSongTitle());
			}
			if($header->GetLessonArtist() !== ""){
				printf("\\title \"%s\"<br>\n",$header->GetSongArtist());
			}
			if($header->GetLessonCopyright() !== ""){
				printf("\\title \"%s\"<br>\n",$header->GetSongCopyright());
			}
		}
	}
	/**
	 * @param Guitar &$guitar
	 * @author Ryan
	 * @since 28/06/2011
	 *
	 * Writes the file information to alphaTex
	 */
	function writeGuitarInfo(Guitar &$guitar){
		//Print out the tuning (This is bad practice what i'm doing and should be fixed
		//inside the ptparser/tuning.php
		printf("\\tuning %s<br>\n",$guitar->GetTuning()->GetLetterAndNumber());
		//Print the capo
		$capo = $guitar->GetCapo();
		if($capo !== 0){
			printf("\\capo %d<br>\n",$capo);
		}
		//Print the instrument MIDI number
		printf("\\instrument %d<br>\n",$guitar->GetPreset());
	}
	/**
	 * @param array &$barlines
	 * @param Barline $barline
	 * @since 28/06/2011
	 * @author Ryan
	 *
	 * Parses a barline and adds it to the bar array
	 * it modifies previous bars if necessary
	 */
	function parseBarline(array &$barlines,Barline $barline){
		$BARTYPES = array("Repeat Open" => "| \\ro ","Repeat Close"=>"\\rc %d ",
							"Bar"=> "|");
		$outString = "";
		if($barline->IsRepeatStart()){
			$outString = $BARTYPES["Repeat Open"];
		}else if($barline->IsRepeatEnd()){
			// This needs to be pushed onto the previous barline due to alphaTab
			// doing things oddly
			$previous = $barlines[count($barlines)-1];
			$barlines[count($barlines)-1] .= sprintf($BARTYPES["Repeat Close"], $barline->GetRepeatCount());
		
			$outString = $BARTYPES["Bar"];
		}else{
			$outString = $BARTYPES["Bar"];
		}
		// Parse the barline paramters
		$outString .= $this->getBarProperties($barline);
		array_push($barlines,$outString);
		
	}
}
?>
